#!/usr/bin/env python
# coding:utf-8

import os
import sys

sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))

import numpy as np
import matrixslow as ms

male_heights = np.random.normal(171, 6, 3)
male_weights = np.random.normal(70, 10, 3)
male_bfrs = np.random.normal(16, 2, 3)
male_labels = [1] * 3

female_heights = np.random.normal(158, 5, 3)
female_weights = np.random.normal(57, 8, 3)
female_bfrs = np.random.normal(22, 2, 3)
female_labels = [-1] * 3

train_set = np.array([np.concatenate((male_heights, female_heights)),
                      np.concatenate((male_weights, female_weights)),
                      np.concatenate((male_bfrs, female_bfrs)),
                      np.concatenate((male_labels, female_labels))]).T

np.random.shuffle(train_set)

# 定义变量
x = ms.core.Variable(dim=(3, 1), init=False, trainable=False)
label = ms.core.Variable(dim=(1, 1), init=False, trainable=False)
w = ms.core.Variable(dim=(1, 3), init=True, trainable=True)
b = ms.core.Variable(dim=(1, 1), init=True, trainable=True)
# 构建计算图
output = ms.ops.Add(ms.ops.MatMul(w, x), b)
predict = ms.ops.Step(output)
# 损失函数
loss = ms.ops.loss.PerceptionLoss(ms.ops.MatMul(label, output))
# 绘制计算图
loss.graph.draw()
# 学习率
learning_rate = 0.0001
# 计算
for epoch in range(10):
    for i in range(len(train_set)):
        # feature 3行1列
        features = np.mat(train_set[i, :-1]).T
        l = np.mat(train_set[i, -1])
        x.set_value(features)
        label.set_value(l)

        loss.forward()
        w.backward(loss)
        b.backward(loss)

        w.set_value(w.value - learning_rate * w.jacobi.T.reshape(w.shape))
        b.set_value(b.value - learning_rate * b.jacobi.T.reshape(b.shape))

        ms.default_graph.clear_jacobi()

    pred = []
    for i in range(len(train_set)):
        features = np.mat(train_set[i, :-1]).T
        x.set_value(features)
        predict.forward()
        pred.append(predict.value.item())
    pred = np.asarray(pred) * 2 - 1
    acc = (train_set[:, -1] == pred).sum() / len(train_set)
    print("epoch: {:d}, accuracy: {:.3f}".format(epoch + 1, acc))
